package com.fp.m7.generator;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import org.apache.commons.lang.time.StopWatch;
import org.joda.time.DateTime;

import com.fp.m7.api.manager.AccountManager;
import com.fp.m7.api.manager.CommentManager;
import com.fp.m7.api.manager.FollowManager;
import com.fp.m7.api.model.Account;
import com.fp.m7.util.MockClock;

public class TestDataGenerator {
	private static final Logger logger = Logger
			.getLogger(TestDataGenerator.class.getName());

	protected final AccountManager accountManager;
	protected final CommentManager commentManager;
	protected final FollowManager followManager;

	protected final MockClock clock;
	protected final Random random;

	protected final AccountGenerator accountGenerator = new AccountGenerator();
	protected final CommentGenerator commentGenerator = new CommentGenerator();

	public TestDataGenerator(AccountManager accountManager,
			CommentManager commentManager, FollowManager followManager,
			MockClock clock, Random random) {

		this.accountManager = accountManager;
		this.commentManager = commentManager;
		this.followManager = followManager;
		this.clock = clock;
		this.random = random;
	}

	public static final Date BEGIN_DATE = new DateTime(2012, 1, 1, 0, 0)
			.toDate();
	public static final Date END_DATE = new DateTime(2013, 1, 1, 0, 0).toDate();

	public List<Serializable> generate(int accountsCount, int commentsCount,
			int followsCount, int unfollowsCount, int updatesCount) {

		logger.info(String
				.format("Generating test data with: %d accounts, %d comments, %d follows and %d unfollows.",
						accountsCount, commentsCount, followsCount,
						unfollowsCount));

		StopWatch stopWatch = new StopWatch();
		stopWatch.start();

		StopWatch total = new StopWatch();
		total.start();

		clock.configure(BEGIN_DATE, END_DATE, accountsCount);

		List<Serializable> accountIds = new ArrayList<Serializable>(
				accountsCount);
		createAccounts(accountsCount, accountIds);

		logger.info(String.format("Finished creating %d accounts in %s.",
				accountsCount, stopWatch));

		stopWatch.reset();
		stopWatch.start();
		clock.configure(BEGIN_DATE, END_DATE, commentsCount);
		createComments(commentsCount, accountIds);

		logger.info(String.format("Finished creating %d comments in %s.",
				commentsCount, stopWatch));

		stopWatch.reset();
		stopWatch.start();
		clock.configure(BEGIN_DATE, END_DATE, followsCount);
		createFollows(followsCount, accountIds);

		logger.info(String.format("Finished creating %d follows in %s.",
				followsCount, stopWatch));

		stopWatch.reset();
		stopWatch.start();
		clock.configure(BEGIN_DATE, END_DATE, unfollowsCount);
		createUnfollows(unfollowsCount, accountIds);

		logger.info(String.format("Finished creating %d unfollows in %s.",
				unfollowsCount, stopWatch));

		stopWatch.reset();
		stopWatch.start();
		clock.configure(BEGIN_DATE, END_DATE, updatesCount);
		createUpdates(updatesCount, accountIds);

		logger.info(String.format("Finished creating %d updates in %s.",
				updatesCount, stopWatch));

		logger.info(String.format(
				"Data generation process took %s in overall.", total));

		return accountIds;
	}

	public void createAccounts(int accountsCount, List<Serializable> accountIds) {
		for (int i = 0; i < accountsCount; ++i) {
			newAccount(accountIds);
		}
	}

	public void createComments(int commentsCount, List<Serializable> accountIds) {
		for (int i = 0; i < commentsCount; ++i) {
			newComment(accountIds);
		}
	}

	public void createFollows(int followsCount, List<Serializable> accountIds) {
		for (int i = 0; i < followsCount; ++i) {
			follow(accountIds);
		}
	}

	public void createUnfollows(int unfollowsCount,
			List<Serializable> accountIds) {
		for (int i = 0; i < unfollowsCount; ++i) {
			unfollow(accountIds);
		}
	}

	public void createUpdates(int updatesCount, List<Serializable> accountIds) {
		for (int i = 0; i < updatesCount; ++i) {
			updateAccount(accountIds);
		}
	}

	public void newAccount(List<Serializable> accountIds) {
		Account newAccount = accountManager.createNew( //
				accountGenerator.getUsername(), //
				accountGenerator.getPassword(), //
				accountGenerator.getNickname());

		accountIds.add(newAccount.getId());
	}

	public void updateAccount(List<Serializable> accountIds) {
		Serializable id = accountIds.get(random.nextInt(accountIds.size()));
		Account found = accountManager.findById(id);
		accountGenerator.updateAccountInfo(found);
		accountManager.update(found);
	}

	public void newComment(List<Serializable> accountIds) {
		Serializable id = accountIds.get(random.nextInt(accountIds.size()));
		Account found = accountManager.findById(id);
		commentManager.createNew(found, commentGenerator.getContent());
	}

	public void follow(List<Serializable> accountIds) {
		Serializable followerId = accountIds.get(random.nextInt(accountIds
				.size()));
		Account follower = accountManager.findById(followerId);

		Serializable followedId = accountIds.get(random.nextInt(accountIds
				.size()));
		Account followed = accountManager.findById(followedId);

		followManager.follow(follower, followed);
	}

	public void unfollow(List<Serializable> accountIds) {
		Serializable followerId = accountIds.get(random.nextInt(accountIds
				.size()));
		Account follower = accountManager.findById(followerId);

		Serializable followedId = accountIds.get(random.nextInt(accountIds
				.size()));
		Account followed = accountManager.findById(followedId);

		followManager.unfollow(follower, followed);
	}

}
